package com.ciaojian.miniapp.api;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import com.ciaojian.core.factory.ObjectMapperFactory;
import com.ciaojian.core.util.ApplicationContextHelper;
import com.ciaojian.core.util.LogUtil;
import com.ciaojian.miniapp.enums.MethodType;
import com.ciaojian.miniapp.enums.MiniAppApiType;
import com.ciaojian.miniapp.model.request.*;
import com.ciaojian.miniapp.model.request.submessage.BaseTemplate;
import com.ciaojian.miniapp.model.response.SubMessageSendResponse;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 微信小程序 api
 *
 * @author Atlakyn
 */
public class WxMiniAppApi {
    public static <T> T urlLinkGenerate(Class<T> clazz) throws JsonProcessingException {
        UrlLinkGenerate urlLinkGenerate = UrlLinkGenerate.builder()
                .path("/pages/my/my")
                .query("")
                .is_expire(Boolean.TRUE)
                .expire_type(1)
                .expire_interval(1)
                .build();
        return HttpHandle.execution(MiniAppApiType.URLLINK_GENERATE, HttpHandle.bean2Map(urlLinkGenerate), clazz);
    }


    /**
     * 登录凭证校验
     *
     * @param request 请求参数
     * @return 返回结果
     * @throws JsonProcessingException Json 处理异常
     */
    public static <T> T code2Session(Code2Session request, Class<T> t) throws JsonProcessingException {
        return HttpHandle.execution(MiniAppApiType.code2session, HttpHandle.bean2Map(request), false, t);
    }

    /**
     * 发送订阅消息
     *
     * @param baseTemplate 消息模板
     * @return 发送结果
     * @throws JsonProcessingException json 转换异常
     */
    public static SubMessageSendResponse subscribeMessageSend(BaseTemplate baseTemplate) throws JsonProcessingException {
        AccessToken accessToken = new AccessToken();
        accessToken.setAppid("wxd2dedecf1c3a890a");
        accessToken.setSecret("7103ce9439295059e89c0ebedcae1cac");
        Map map = HttpHandle.execution(MiniAppApiType.ACCESS_TOKEN, BeanUtil.beanToMap(accessToken), false, Map.class);

        return HttpHandle.execution(MiniAppApiType.SUBSCRIBE_MESSAGE_SEND,
                baseTemplate.toJsonStr(),
                String.valueOf(map.get("access_token")),
                SubMessageSendResponse.class);
    }

    /**
     * 发送订阅消息
     *
     * @param baseTemplate 消息模板
     * @param accessToken  后台接口调用凭据
     * @return 发送结果
     * @throws JsonProcessingException json 转换异常
     */
    public static SubMessageSendResponse subscribeMessageSend(BaseTemplate baseTemplate, AccessToken accessToken) throws JsonProcessingException {
        Map map = HttpHandle.execution(MiniAppApiType.ACCESS_TOKEN, BeanUtil.beanToMap(accessToken), false, Map.class);

        return HttpHandle.execution(MiniAppApiType.SUBSCRIBE_MESSAGE_SEND,
                baseTemplate.toJsonStr(),
                String.valueOf(map.get("access_token")),
                SubMessageSendResponse.class);
    }

    /**
     * 获取小程序码
     *
     * @param params 参数
     * @return 小程序码路径
     * @throws JsonProcessingException JSON 转换异常
     */
    public static String getUnlimited(QRCodeUnlimited params) throws IOException {
        ObjectMapper om = ObjectMapperFactory.getObjectMapperInstance();
        om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        String string = om.writeValueAsString(params);
        return HttpHandle.executionQRCode(MiniAppApiType.QR_CODE_UNLIMITED, string);
    }

}

class HttpHandle {
    /**
     * 执行请求
     *
     * @return 小程序码
     */
    static String executionQRCode(MiniAppApiType type, Object params) throws IOException {
        return HttpHandle.doPost(type.getUrl().concat("?access_token=" + Objects.requireNonNull(getAccessToken())), params.toString());
    }

    /**
     * 执行请求
     *
     * @return 请求结果
     */
    static <T> T execution(MiniAppApiType type, Object params, Class<T> clazz) throws JsonProcessingException {
        return execution(type, params, true, clazz);
    }


    /**
     * 执行请求
     *
     * @return 请求结果
     */
    static <T> T execution(MiniAppApiType type, Object params, Boolean requireToken, Class<T> clazz) throws JsonProcessingException {
        MethodType methodType = type.getMethodType();
        // 请求地址
        String url = type.getUrl();
        if (requireToken) {
            url = url.concat("?access_token=" + getAccessToken());
        }
        // 请求结果
        T t = null;
        if (methodType.equals(MethodType.GET)) {
            t = HttpHandle.doGet(url, (Map<String, Object>) params, clazz);
        }
        if (methodType.equals(MethodType.POST)) {
            if (params instanceof Map) {
                t = HttpHandle.doPost(url, (HashMap<String, Object>) params, clazz);
            }
            if (params instanceof String) {
                System.err.println(params);
                t = HttpHandle.doPost(url, params.toString(), clazz);
            }
        }
        return t;
    }

    /**
     * 执行请求
     *
     * @param token 传入 token
     * @return 请求结果
     */
    static <T> T execution(MiniAppApiType type, Object params, String token, Class<T> clazz) throws JsonProcessingException {
        MethodType methodType = type.getMethodType();
        // 请求地址
        String url = type.getUrl();
        url = url.concat("?access_token=" + token);
        // 请求结果
        T t = null;
        if (methodType.equals(MethodType.GET)) {
            t = HttpHandle.doGet(url, (Map<String, Object>) params, clazz);
        }
        if (methodType.equals(MethodType.POST)) {
            if (params instanceof Map) {
                t = HttpHandle.doPost(url, (HashMap<String, Object>) params, clazz);
            }
            if (params instanceof String) {
                t = HttpHandle.doPost(url, params.toString(), clazz);
            }
        }
        return t;
    }

    /**
     * 获取 access_token
     *
     * @return 微信返回的 access_token
     */
    private static String getAccessToken() throws JsonProcessingException {
        AccessToken accessToken = (AccessToken) ApplicationContextHelper.getBean("miniAccessToken");
        Map map = execution(MiniAppApiType.ACCESS_TOKEN, BeanUtil.beanToMap(accessToken), false, Map.class);

        if (map != null) {
            return String.valueOf(map.get("access_token"));
        }
        return null;
    }

    static Map<String, Object> bean2Map(BaseRequest baseRequest) {
        return BeanUtil.beanToMap(baseRequest, false, true);
    }

    private static <T> T doGet(String url, Map<String, Object> params, Class<T> t) throws JsonProcessingException {
        String result = HttpUtil.get(url, params);
        return new ObjectMapper().readValue(result, t);
    }

    private static <T> T doPost(String url, Map<String, Object> params, Class<T> t) throws JsonProcessingException {
        String result = HttpUtil.post(url, params);
        return new ObjectMapper().readValue(result, t);
    }

    private static <T> T doPost(String url, String params, Class<T> t) throws JsonProcessingException {
        String result = HttpUtil.post(url, params);
        return new ObjectMapper().readValue(result, t);
    }

    /**
     * 接收返回值为 IO 流
     *
     * @param urlString 请求URL
     * @param params    参数
     * @return 图片路径
     */
    private static String doPost(String urlString, String params) throws IOException {
        URL url = new URL(urlString);
        HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
        // 提交模式
        httpUrlConnection.setRequestMethod("POST");
        // 发送POST请求必须设置如下两行
        httpUrlConnection.setDoOutput(true);
        httpUrlConnection.setDoInput(true);
        // 获取URLConnection对象对应的输出流
        PrintWriter printWriter = new PrintWriter(httpUrlConnection.getOutputStream());
        // 发送请求参数
        printWriter.write(params);
        // flush输出流的缓冲
        printWriter.flush();

        //buff用于存放循环读取的临时数据
        //开始获取数据
        BufferedInputStream bis = new BufferedInputStream(httpUrlConnection.getInputStream());
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc;
        while ((rc = bis.read(buff, 0, 100)) > 0) {
            swapStream.write(buff, 0, rc);
        }
        ByteArrayInputStream inputStream = new ByteArrayInputStream(swapStream.toByteArray());
        BufferedImage image = ImageIO.read(inputStream);
        image.flush();
        String name = IdUtil.randomUUID();
        String newImg = "/www/server/chaojian/img/userQRCode/" + name + ".jpg";
//        String newImg = "/Users/oxiny/Desktop/" + name + ".jpg";
        boolean i = ImageIO.write(image, "jpg", new File(newImg));
        if (!i) {
            LogUtil.writeBusinessErrorLog("生成失败,参数:{}", params);
            throw new RuntimeException("生成失败");
        }
        return "https://www.ciaojian.com/img/userQRCode/" + name + ".jpg";
    }
}
